﻿#region Copyright

// // The contents of this file are subject to the Mozilla Public License
// // Version 1.1 (the "License"); you may not use this file except in compliance
// // with the License. You may obtain a copy of the License at
// //   
// // http://www.mozilla.org/MPL/
// //   
// // Software distributed under the License is distributed on an "AS IS"
// // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// // License for the specific language governing rights and limitations under 
// // the License.
// //   
// // The Initial Developer of the Original Code is Robert Smyth.
// // Portions created by Robert Smyth are Copyright (C) 2008-2013.
// //   
// // All Rights Reserved.

#endregion

using System.Collections.Generic;
using NoeticTools.NWorkflow.DotNetInterop;
using NoeticTools.NWorkflow.Exceptions;
using NoeticTools.NWorkflow.ProgressMonitoring;


namespace NoeticTools.NWorkflow.Scopes
{
    public class OnBeginEndScope<T>
    {
        private readonly List<Action> _actions = new List<Action>();
        private readonly List<IActivity> _runningActivities = new List<IActivity>();
        private readonly IWorkflowRules _workflowRules;
        private string _initialStep;

        public OnBeginEndScope(IWorkflowRules workflowRules)
        {
            _workflowRules = workflowRules;
        }

        // todo - lifecycle is wrong
        public IActivityListener<T> Runner { get; set; }

        public object SyncRoot
        {
            get { return _workflowRules.SyncRoot; }
        }

        public ThenScope<OnBeginEndScope<T>> EnterStep(string stepName)
        {
            if (!string.IsNullOrEmpty(_initialStep))
            {
                throw new WorkflowConfigurationException();
            }
            _initialStep = stepName;
            _actions.Add(() => _workflowRules.TransitionTo(stepName));

            return new ThenScope<OnBeginEndScope<T>>(this);
        }

        /// <summary>
        ///     Start a workflow activity.
        /// </summary>
        /// <remarks>
        ///     Runs a workflow action. The activity will be allowed to run until the workflow
        ///     completes or is cancelled.
        /// </remarks>
        public ThenScope<OnBeginEndScope<T>> Start(IActivity<T> activity)
        {
            _actions.Add(() => StartActivity(activity));
            return new ThenScope<OnBeginEndScope<T>>(this);
        }

        private void StartActivity(IActivity<T>  activity)
        {
            lock (SyncRoot)
            {
                _runningActivities.Add(activity);
                activity.Begin(new ActivityLifecycleController<T>(activity, this, Runner));
            }
        }

        private void EnsureCondition(Func<bool> condition)
        {
            if (!condition())
            {
                throw new WorkflowException("Pre/post condition failed.");
            }
        }

        public void Run()
        {
            lock (SyncRoot)
            {
                foreach (var action in _actions)
                {
                    action();
                }
            }
        }

        public void Stop()
        {
            lock (SyncRoot)
            {
                var copy = new List<IActivity>(_runningActivities);
                foreach (var activity in copy)
                {
                    activity.Cancel();
                }
            }
        }

        /// <summary>
        ///     Ensure pre/post condition is true.
        /// </summary>
        public ThenScope<OnBeginEndScope<T>> Ensure(Func<bool> condition)
        {
            _actions.Add(() => EnsureCondition(condition));
            return new ThenScope<OnBeginEndScope<T>>(this);
        }

        private class ActivityLifecycleController<T2> : IActivityListener<T2> 
        {
            private readonly IActivity _activity;
            private readonly OnBeginEndScope<T2> _onBeginScope;
            private readonly IActivityListener<T2> _listener;

            public ActivityLifecycleController(IActivity activity, OnBeginEndScope<T2> onBeginScope, IActivityListener<T2> listener)
            {
                _activity = activity;
                _onBeginScope = onBeginScope;
                _listener = listener;
            }

            public void OnProgress(string stepName, IProgress progress)
            {
                _listener.OnProgress(stepName, progress);
            }

            public void OnProgress(IActivityProgress progress)
            {
                _listener.OnProgress(progress);
            }

            public void OnCompleted(IActivity<T2> activity)
            {
                lock (_onBeginScope.SyncRoot)
                {
                    _onBeginScope._runningActivities.Remove(_activity);
                }
            }
        }

        internal class HasInitialStepValidationRule : IValidationRule
        {
            private readonly OnBeginEndScope<T> _onBeginScope;

            public HasInitialStepValidationRule(OnBeginEndScope<T> onBeginScope)
            {
                _onBeginScope = onBeginScope;
            }

            public bool Validate<TResult>(WorkflowRules<TResult> rules)
            {
                return !string.IsNullOrEmpty(_onBeginScope._initialStep);
            }
        }
    }
}